package com.zbangmall.util.common;

import org.apache.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * <h6>Description:<h6>
 * <p>Java Class与反射相关的一些工具类</p>
 */
public final class ClassUtil {


	/**
	 * 获取类加载器
	 */
	public static ClassLoader overridenClassLoader;
	private static Logger logger = Logger.getLogger(ClassUtil.class);

	public static ClassLoader getContextClassLoader() {
		return overridenClassLoader != null ?
				overridenClassLoader : Thread.currentThread().getContextClassLoader();
	}

	/**
	 * 获取指定类的全部属性字段
	 *
	 * @param className    需要获取的类名
	 * @param extendsField 是否获取接口或父类中的公共属性
	 * @return 属性字段数组
	 */
	public final static String[] getField(String className, boolean extendsField) {
		Class       classz = loadClass(className);
		Field[]     fields = classz.getFields();
		Set<String> set    = new HashSet<>();
		if (fields != null) {
			for (Field f : fields) {
				set.add(f.getName());
			}
		}
		if (extendsField) {
			Field[] fieldz = classz.getDeclaredFields();
			if (fieldz != null) {
				for (Field f : fieldz) {
					set.add(f.getName());
				}
			}
		}
		return set.toArray(new String[set.size()]);
	}

	/**
	 * 获取类中的公共属性
	 *
	 * @param className    需要获取的类名
	 * @param extendsField 是否获取接口或父类中的公共属性
	 * @return 属性字段数组
	 */
	public final static String[] getPublicField(String className, boolean extendsField) {
		Class       classz = loadClass(className);
		Set<String> set    = new HashSet<>();
		Field[]     fields = classz.getDeclaredFields();
		if (fields != null) {
			for (Field f : fields) {
				String modifier = Modifier.toString(f.getModifiers());
				if (modifier.startsWith("public")) {
					set.add(f.getName());
				}
			}
		}
		if (extendsField) {
			Field[] fieldz = classz.getFields();
			if (fieldz != null) {
				for (Field f : fieldz) {
					set.add(f.getName());
				}
			}
		}
		return set.toArray(new String[set.size()]);
	}

	/**
	 * 获取类中定义的protected类型的属性字段
	 *
	 * @param className 需要获取的类名
	 * @return protected类型的属性字段数组
	 */
	public final static String[] getProtectedField(String className) {
		Class       classz = loadClass(className);
		Set<String> set    = new HashSet<>();
		Field[]     fields = classz.getDeclaredFields();
		if (fields != null) {
			for (Field f : fields) {
				String modifier = Modifier.toString(f.getModifiers());
				if (modifier.startsWith("protected")) {
					set.add(f.getName());
				}
			}
		}
		return set.toArray(new String[set.size()]);
	}

	/**
	 * 获取类中定义的private类型的属性字段
	 *
	 * @param className 需要获取的类名
	 * @return private类型的属性字段数组
	 */
	public final static String[] getPrivateField(String className) {
		Class       classz = loadClass(className);
		Set<String> set    = new HashSet<>();
		Field[]     fields = classz.getDeclaredFields();
		if (fields != null) {
			for (Field f : fields) {
				String modifier = Modifier.toString(f.getModifiers());
				if (modifier.startsWith("private")) {
					set.add(f.getName());
				}
			}
		}
		return set.toArray(new String[set.size()]);
	}

	/**
	 * 获取对象的全部public类型方法
	 *
	 * @param className     需要获取的类名
	 * @param extendsMethod 是否获取继承来的方法
	 * @return 方法名数组
	 */
	public final static String[] getPublicMethod(String className, boolean extendsMethod) {
		Class    classz = loadClass(className);
		Method[] methods;
		if (extendsMethod) {
			methods = classz.getMethods();
		} else {
			methods = classz.getDeclaredMethods();
		}
		Set<String> set = new HashSet<>();
		if (methods != null) {
			for (Method f : methods) {
				String modifier = Modifier.toString(f.getModifiers());
				if (modifier.startsWith("public")) {
					set.add(f.getName());
				}
			}
		}
		return set.toArray(new String[set.size()]);
	}


	/**
	 * 获取对象的全部protected类型方法
	 *
	 * @param className     需要获取的类名
	 * @param extendsMethod 是否获取继承来的方法
	 * @return 方法名数组
	 */
	public final static String[] getProtectedMethod(String className, boolean extendsMethod) {
		Class    classz = loadClass(className);
		Method[] methods;
		if (extendsMethod) {
			methods = classz.getMethods();
		} else {
			methods = classz.getDeclaredMethods();
		}
		Set<String> set = new HashSet<>();
		if (methods != null) {
			for (Method f : methods) {
				String modifier = Modifier.toString(f.getModifiers());
				if (modifier.startsWith("protected")) {
					set.add(f.getName());
				}
			}
		}
		return set.toArray(new String[set.size()]);
	}

	/**
	 * 获取对象的全部private类型方法
	 *
	 * @param className 需要获取的类名
	 * @return 方法名数组
	 */
	public final static String[] getPrivateMethod(String className) {
		Class       classz  = loadClass(className);
		Method[]    methods = classz.getDeclaredMethods();
		Set<String> set     = new HashSet<>();
		if (methods != null) {
			for (Method f : methods) {
				String modifier = Modifier.toString(f.getModifiers());
				if (modifier.startsWith("private")) {
					set.add(f.getName());
				}
			}
		}
		return set.toArray(new String[set.size()]);
	}

	/**
	 * 获取对象的全部方法
	 *
	 * @param className     需要获取的类名
	 * @param extendsMethod 是否获取继承来的方法
	 * @return 方法名数组
	 */
	public final static String[] getMethod(String className, boolean extendsMethod) {
		Class    classz = loadClass(className);
		Method[] methods;
		if (extendsMethod) {
			methods = classz.getMethods();
		} else {
			methods = classz.getDeclaredMethods();
		}
		Set<String> set = new HashSet<>();
		if (methods != null) {
			for (Method f : methods) {
				set.add(f.getName());
			}
		}
		return set.toArray(new String[set.size()]);
	}


	/**
	 * 调用对象的setter方法
	 *
	 * @param obj   对象
	 * @param att   属性名
	 * @param value 属性值
	 * @param type  属性类型
	 */
	public final static void setter(Object obj, String att, Object value, Class<?> type)
			throws InvocationTargetException, IllegalAccessException {
		try {
			String name = att.substring(0, 1).toUpperCase() + att.substring(1);
			Method met  = obj.getClass().getMethod("set" + name, type);
			met.invoke(obj, value);
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}

	}


	/**
	 * 获取指定目录下所有的类名
	 *
	 * @param path         包名
	 * @param childPackage 是否获取子包
	 */
	public final static List<String> getClassName(String path, boolean childPackage) {
		List<String> fileNames = new ArrayList<>();
		if (path.endsWith(".jar")) {
			fileNames.addAll(getClassNameByJar(path));
		} else {
			fileNames = getClassNameByFile(path, childPackage);
		}
		return fileNames;
	}

	/**
	 * 从项目文件获取某包下所有类
	 *
	 * @param filePath     文件路径
	 * @param childPackage 是否遍历子包
	 * @return 类的完整名称
	 */
	public final static List<String> getClassNameByFile(String filePath, boolean childPackage) {
		List<String> myClassName = new ArrayList<>();
		List<File>   files       = FileUtil.listFile(filePath, childPackage);
		for (File file : files) {
			if (file.getName().endsWith(".class")) {
				String childFilePath = file.getPath();
				int    index         = filePath.replaceAll("\\\\", ".").length();
				childFilePath = childFilePath.replaceAll("\\\\", ".").substring(index, childFilePath.length());
				myClassName.add(childFilePath);
			}
		}
		return myClassName;
	}

	/**
	 * 从jar获取某包下所有类
	 *
	 * @param jarPath jar文件路径
	 * @return 类的完整名称
	 */
	public final static List<String> getClassNameByJar(String jarPath) {
		List<String> myClassName = new ArrayList<>();
		try (JarFile jarFile = new JarFile(jarPath)) {
			Enumeration<JarEntry> entrys = jarFile.entries();
			while (entrys.hasMoreElements()) {
				JarEntry jarEntry  = entrys.nextElement();
				String   entryName = jarEntry.getName();
				if (entryName.endsWith(".class")) {
					entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
					myClassName.add(entryName);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return myClassName;
	}


	/**
	 * 加载指定的类
	 *
	 * @param className 需要加载的类
	 * @return 加载后的类
	 */
	public final static Class loadClass(String className) {
		Class theClass = null;
		try {
			theClass = Class.forName(className);
		} catch (ClassNotFoundException e1) {
			logger.error("load class error:" + e1.getMessage());
			e1.printStackTrace();
		}
		return theClass;
	}

	/**
	 * 获取jar包中的非*.class外的全部资源文件名字
	 *
	 * @param jarPath jar文件路径
	 * @return 返回资源名称数组
	 */
	public final static List<String> getResourceNameByJar(String jarPath) {
		List<String> resource = new ArrayList<>();
		try (JarFile jarFile = new JarFile(jarPath)) {
			Enumeration<JarEntry> entrys = jarFile.entries();
			while (entrys.hasMoreElements()) {
				JarEntry jarEntry  = entrys.nextElement();
				String   entryName = jarEntry.getName();
				if (!entryName.endsWith(".class") && !entryName.endsWith("/")) {
					resource.add(FilePathUtil.commandPath(jarPath) + "!" + entryName);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return resource;
	}

	/**
	 * 获取jar包中的非*.class外的全部的以suffix结尾的资源文件
	 *
	 * @param jarPath jar包的路径
	 * @param suffix  后缀名称
	 * @return 返回资源名称数组
	 */
	public final static List<String> getResourceNameByJar(String jarPath, String suffix) {
		List<String> resource = new ArrayList<>();
		try (JarFile jarFile = new JarFile(jarPath)) {
			Enumeration<JarEntry> entrys = jarFile.entries();
			while (entrys.hasMoreElements()) {
				JarEntry jarEntry  = entrys.nextElement();
				String   entryName = jarEntry.getName();
				if (entryName.endsWith(suffix)) {
					resource.add(FilePathUtil.commandPath(jarPath) + "!" + entryName);
				}
			}
		} catch (IOException e) {

			logger.error(ExceptionUtil.stackTraceToString(e, Thread.currentThread()));
		}
		return resource;
	}

	/**
	 * 获取一个类的父类
	 *
	 * @param className 需要获取的类
	 * @return 父类的名称
	 */
	public final static String getSuperClass(String className) {
		Class classz     = loadClass(className);
		Class superclass = classz.getSuperclass();
		return superclass.getName();
	}

	/**
	 * 获取一个雷的继承链
	 *
	 * @param className 需要获取的类
	 * @return 继承类名的数组
	 */
	public final static String[] getSuperClassChian(String className) {
		Class        classz     = loadClass(className);
		List<String> list       = new ArrayList<>();
		Class        superclass = classz.getSuperclass();
		String       superName  = superclass.getName();
		if (!"java.lang.Object".equals(superName)) {
			list.add(superName);
			list.addAll(Arrays.asList(getSuperClassChian(superName)));
		} else {
			list.add(superName);
		}
		return list.toArray(new String[list.size()]);
	}

	/**
	 * 获取一类实现的全部接口
	 *
	 * @param className         需要获取的类
	 * @param extendsInterfaces 话说getInterfaces能全部获取到才对，然而测试的时候父类的接口并没有
	 *                          因此就多除了这参数
	 * @return 实现接口名称的数组
	 */
	public final static String[] getInterfaces(String className, boolean extendsInterfaces) {
		Class        classz     = loadClass(className);
		List<String> list       = new ArrayList<>();
		Class[]      interfaces = classz.getInterfaces();
		if (interfaces != null) {
			for (Class inter : interfaces) {
				list.add(inter.getName());
			}
		}
		if (extendsInterfaces) {
			String[] superClass = getSuperClassChian(className);
			for (String c : superClass) {
				list.addAll(Arrays.asList(getInterfaces(c, false)));
			}
		}
		return list.toArray(new String[list.size()]);
	}
}
